package timing.ukulele.portal.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import timing.ukulele.common.constant.RoleConstant;
import timing.ukulele.common.data.ResponseCode;
import timing.ukulele.common.data.ResponseData;
import timing.ukulele.data.portal.view.RoleVO;
import timing.ukulele.data.portal.view.SysResourceVO;
import timing.ukulele.data.portal.view.SysTenantUserVO;
import timing.ukulele.persistence.service.BaseService;
import timing.ukulele.portal.convertor.ResourceConvertor;
import timing.ukulele.portal.convertor.RoleConvertor;
import timing.ukulele.portal.mapper.AntRoleMenuMapper;
import timing.ukulele.portal.mapper.SysResourceMapper;
import timing.ukulele.portal.mapper.SysRoleMapper;
import timing.ukulele.portal.persistent.*;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class SysRoleService extends BaseService<SysRoleMapper, SysRole> {
    private final SysResourceMapper resourceMapper;
    private final SysTenantService tenantService;
    private final AntRoleMenuMapper roleMenuMapper;

    public SysRoleService(SysResourceMapper resourceMapper, SysTenantService tenantService, AntRoleMenuMapper roleMenuMapper) {
        this.resourceMapper = resourceMapper;
        this.tenantService = tenantService;
        this.roleMenuMapper = roleMenuMapper;
    }

    public ResponseData<RoleVO> addRoleFollowCopyMenuAndResource(String currentUser, RoleVO role) {
        // 添加角色数据
        SysRole roleData = RoleConvertor.INSTANCE.v2p(role);
        roleData.setCode(roleData.getCode().toUpperCase());
        this.save(roleData);
        // 根据编码拷贝角色数据，先查询当前编码已经存在的数据，然后将该存在角色的对应菜单和资源进行拷贝
        Optional<SysRole> roleOptional = this.lambdaQuery().eq(SysRole::getTenantId, 0L).eq(SysRole::getCode, role.getCode()).list().stream().findFirst();
        if (roleOptional.isPresent()) {
            SysRole dbExistSysRole = roleOptional.get();
            // 查询当前角色已配置的菜单
            List<AntRoleMenu> roleMenuList = this.roleMenuMapper.selectRoleMenu(dbExistSysRole.getId());
            if (!CollectionUtils.isEmpty(roleMenuList)) {
                roleMenuList.forEach(item -> {
                    item.setRoleId(roleData.getId());
                    item.setTenantId(roleData.getTenantId());
                });
                roleMenuMapper.batchAddRoleMenu(roleMenuList);
            }
            // 查询当前角色已经配置的权限
            List<SysRoleMenuPermission> rolePermissionList = this.roleMenuMapper.getRolePermission(dbExistSysRole.getId());
            if (!CollectionUtils.isEmpty(rolePermissionList)) {
                rolePermissionList.forEach(item -> {
                    item.setRoleId(roleData.getId());
                    item.setTenantId(role.getTenantId());
                });
                roleMenuMapper.batchAddRolePermission(rolePermissionList);
            }
            // 查询当前角色已经配置的资源
            List<SysRoleResource> roleResourceList = this.baseMapper.getRoleResourceByRoleId(dbExistSysRole.getId());
            if (!CollectionUtils.isEmpty(roleResourceList)) {
                roleResourceList.forEach(item -> {
                    item.setRoleId(roleData.getId());
                    item.setTenantId(role.getTenantId());
                });
                this.baseMapper.batchAddRoleResource(roleResourceList);
            }
        }
        RoleVO vo = RoleConvertor.INSTANCE.p2v(roleData);
        return ResponseData.success(vo);
    }

    /**
     * 注册成功的用户，设置成USER角色
     *
     * @param userId 用户id
     */
    public boolean setUserRegisteredRole(Long userId, String username) {
        //给用户设置角色
        List<SysRole> userRoles = this.lambdaQuery().eq(SysRole::getCode, RoleConstant.USER).list();
        if (!CollectionUtils.isEmpty(userRoles))
            this.addUserRole(userId, username, userRoles.get(0).getId(), 0L);
        return true;
    }

    /**
     * 获取用户已有的角色
     *
     * @param userId 用户id
     * @return 角色列表
     */
    public List<RoleVO> getUserRole(Long userId, Long tenantId) {
        List<SysUserRole> userRoles = this.getBaseMapper().getUserRole(userId, tenantId == null ? 0L : tenantId);
        if (!CollectionUtils.isEmpty(userRoles)) {
            Set<Long> roles = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet());
            List<SysRole> list = this.lambdaQuery().in(SysRole::getId, roles).list();
            if (!CollectionUtils.isEmpty(list)) {
                List<RoleVO> voList = RoleConvertor.INSTANCE.p2vList(list);
                return voList;
            }
        }
        return null;
    }

    public Boolean deleteUserRole(Long userId, Long roleId, Long tenantId) {
        return this.getBaseMapper().deleteUserRole(userId, roleId, tenantId == null ? 0L : tenantId) >= 0;
    }

    public Boolean addUserRole(Long userId, String username, Long roleId, Long tenantId) {
        int count = this.getBaseMapper().existUserRole(userId, roleId, tenantId == null ? 0L : tenantId);
        if (count > 0) {
            return false;
        }
        return this.getBaseMapper().addUserRole(userId, username, roleId, tenantId == null ? 0L : tenantId) > 0;
    }

//    @Transactional(rollbackFor = Exception.class)
//    public ResponseData<Boolean> addTenantUserRole(Long userId, String username, List<String> roleCode, Long tenantId) {
//        ResponseData<Boolean> response = new ResponseData<>();
//        List<SysRole> roleList = this.lambdaQuery().eq(SysRole::getTenantId, 0L).eq(SysRole::getDeleted, Boolean.FALSE).in(SysRole::getCode, roleCode).select(SysRole::getId).list();
//        if (CollectionUtils.isEmpty(roleList)) {
//            response.setCode(ResponseCode.ERROR.getCode());
//            response.setMessage("角色不存在");
//            return response;
//        }
//        // 删除用户在当前机构下的角色
//        this.getBaseMapper().deleteUserTenantRole(userId, tenantId);
//        Set<Long> roleIds = roleList.stream().map(SysRole::getId).collect(Collectors.toSet());
//        this.getBaseMapper().batchAddUserTenantRole(userId, username, roleIds, tenantId);
//        response.setCode(ResponseCode.SUCCESS.getCode());
//        return response;
//    }
//
//    public ResponseData<Boolean> deleteTenantUserRole(Long userId, Long tenantId) {
//        // 删除用户在当前机构下的角色
//        this.getBaseMapper().deleteUserTenantRole(userId, tenantId);
//        ResponseData<Boolean> response = new ResponseData<>();
//        response.setCode(ResponseCode.SUCCESS.getCode());
//        return response;
//    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseData<Boolean> changeTenantUserRole(Long userId, String username, Set<Long> roleList, Long tenantId) {
        ResponseData<Boolean> response = new ResponseData<>();
//        List<SysRole> roleList = this.lambdaQuery().eq(SysRole::getTenantId, tenantId).eq(SysRole::getDeleted, Boolean.FALSE).in(SysRole::getId, roleId).select(SysRole::getId).list();
//        if (CollectionUtils.isEmpty(roleList)) {
//            response.setCode(ResponseCode.ERROR.getCode());
//            response.setMessage("角色不存在");
//            return response;
//        }
        // 删除用户在当前机构下的角色
        this.getBaseMapper().deleteUserTenantRole(userId, tenantId);
//        Set<Long> roleIds = roleList.stream().map(SysRole::getId).collect(Collectors.toSet());
        this.getBaseMapper().batchAddUserTenantRole(userId, username, roleList, tenantId);
        SysTenantUserVO vo = new SysTenantUserVO();
        vo.setTenantId(tenantId);
        vo.setUserId(userId);
        vo.setUsername(username);
        this.tenantService.addTenantUser(vo);
        response.setCode(ResponseCode.SUCCESS.getCode());
        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    public ResponseData<Boolean> addUserRoleAndTenant(String currentUser, Long userId, String username, Long roleId, Long tenantId) {

        int count = this.getBaseMapper().existUserRole(userId, roleId, tenantId == null ? 0L : tenantId);
        if (count > 0) {
            return ResponseData.error("当前用户已存在该角色，请勿重复添加");
        }
        this.getBaseMapper().addUserRole(userId, username, roleId, tenantId == null ? 0L : tenantId);

        SysTenantUserVO vo = new SysTenantUserVO();
        vo.setTenantId(tenantId);
        vo.setUserId(userId);
        vo.setUsername(username);
        this.tenantService.addTenantUser(vo);

        return ResponseData.success(Boolean.TRUE);
    }

    public IPage<RoleVO> getPage(String code, String name, String description, Integer type, List<Integer> typeList, Boolean deleted, int current, int size) {
        Page<SysRole> page = new Page<>(current, size);
        LambdaQueryChainWrapper<SysRole> query = lambdaQuery();
        if(StringUtils.hasText(code)) {
            query.like(SysRole::getCode, code.toUpperCase());
        }
        query.like(StringUtils.hasText(name), SysRole::getName, name);
        query.like(StringUtils.hasText(description), SysRole::getDescription, description);
        query.eq(type != null, SysRole::getType, type);
        query.in(typeList != null, SysRole::getType, typeList);
        query.eq(deleted != null, SysRole::getDeleted, deleted);
        query.orderByDesc(SysRole::getId);
        IPage<SysRole> iPage = query.page(page);
        Page<RoleVO> voPage = new Page<>(current, size);
        if (iPage != null && !CollectionUtils.isEmpty(iPage.getRecords())) {
            List<RoleVO> voList = RoleConvertor.INSTANCE.p2vList(iPage.getRecords());
            voPage.setRecords(voList);
            voPage.setTotal(iPage.getTotal());
        }
        return voPage;
    }


    public IPage<SysUserRole> getRoleUserPage(Long roleId, String username, int current, int size) {
        Page<SysUserRole> page = new Page<>(current, size);
        IPage<SysUserRole> iPage = null;
        if (StringUtils.hasText(username)) {
            iPage = this.getBaseMapper().getRoleUserPageWithUsername(page, roleId, username);
        } else {
            iPage = this.getBaseMapper().getRoleUserPage(page, roleId);
        }
        return iPage;
    }

    public List<SysResourceVO> findByRole(Long roleId) {
        List<SysResource> list = this.getBaseMapper().getRoleResourceList(roleId);
        if (CollectionUtils.isEmpty(list))
            return null;
        List<SysResourceVO> voList = ResourceConvertor.INSTANCE.p2vList(list);
        return voList;
    }

    public List<SysResourceVO> findByRoles(List<Long> roleIdList) {
        List<SysResource> list = this.getBaseMapper().getRoleResourceListByIds(roleIdList);
        if (CollectionUtils.isEmpty(list))
            return null;
        List<SysResourceVO> voList = ResourceConvertor.INSTANCE.p2vList(list);
        return voList;
    }

    public void addRoleResource(Long roleId, List<Long> resourceIdList) {
        SysRole role = this.getById(roleId);
        if (role == null)
            return;
        List<SysRoleResource> list = new ArrayList<>(resourceIdList.size());
        for (Long resourceId : resourceIdList) {
            SysRoleResource po = new SysRoleResource();
            po.setResourceId(resourceId);
            po.setRoleId(roleId);
            po.setTenantId(role.getTenantId());
            list.add(po);
        }
        this.getBaseMapper().batchAddRoleResource(list);
    }

    public void deleteRoleResource(Long roleId, Long resourceId) {
        this.getBaseMapper().deleteOneRoleResource(roleId, resourceId);
    }

    public Map<String, Set<String>> getUserResource(String username, Long tenantId) {
        List<Long> roles = this.baseMapper.getUserRoleByUsername(username, tenantId);
        if (CollectionUtils.isEmpty(roles)) {
            return null;
        }
        List<SysResource> resourcesList = this.getBaseMapper().getRoleResourceListByIds(roles);
        if (CollectionUtils.isEmpty(resourcesList)) {
            return null;
        }
        Map<String, Set<String>> result = new HashMap<>(resourcesList.size());
        for (SysResource resource : resourcesList) {
            Set<String> methods = result.computeIfAbsent(resource.getResource(), k -> new HashSet<>(5));
            methods.add(resource.getMethod());
        }
        return result;
    }

    public IPage<SysResourceVO> getResourcePage(Long roleId, Long groupId, String resource, String remark, String method, Boolean deleted, int current, int size) {
        Page<SysResource> page = new Page<>(current, size);
        LambdaQueryWrapper<SysResource> query = new LambdaQueryWrapper<>();
        query.eq(groupId != null, SysResource::getGroupId, groupId);
        query.eq(StringUtils.hasText(resource), SysResource::getResource, resource);
        query.eq(StringUtils.hasText(remark), SysResource::getRemark, remark);
        query.eq(StringUtils.hasText(method), SysResource::getMethod, method);
        query.eq(deleted != null, SysResource::getDeleted, deleted);
        query.orderByDesc(SysResource::getId);
        IPage<SysResource> iPage = this.resourceMapper.selectPage(page, query);
        Page<SysResourceVO> voPage = new Page<>(current, size);
        if (iPage == null) {
            return voPage;
        }
        List<SysResource> records = iPage.getRecords();
        if (!CollectionUtils.isEmpty(records)) {
            List<Long> ids = records.stream().map(SysResource::getId).collect(Collectors.toList());
            List<Long> roleResource = this.baseMapper.getRoleResource(roleId, ids);
            List<SysResourceVO> voList = new ArrayList<>(records.size());
            iPage.getRecords().forEach(po -> {
                SysResourceVO vo = ResourceConvertor.INSTANCE.p2v(po);
                if (!CollectionUtils.isEmpty(roleResource))
                    vo.setChecked(roleResource.contains(vo.getId()));
                voList.add(vo);
            });
            voPage.setRecords(voList);
            voPage.setTotal(iPage.getTotal());
        }
        return voPage;
    }

    public List<RoleVO> findRolesByIds(List<Long> roleIdList) {
        List<SysRole> list = this.lambdaQuery().in(SysRole::getId, roleIdList).list();
        List<RoleVO> roleVOS = RoleConvertor.INSTANCE.p2vList(list);
        return roleVOS;
    }
}
